﻿using System.IO;
using JasonSoft.ScriptAnnotations;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Linq;
using System.Reflection;

namespace JasonSoft.ScriptAnnotations
{
    class ValidatorStrategyFactory
    {
        public ValidatorStrategyFactory()
        {
            var catalog = new AggregateCatalog();
            catalog.Catalogs.Add(new DirectoryCatalog(AssemblyDirectory));

            var container = new CompositionContainer(catalog);

            container.ComposeParts(this);
        }

        [ImportMany]
        IEnumerable<Lazy<IValidatorStrategy, IValidatorStrategyData>> validatorStrategies;

        public IValidatorStrategy GetValidatorStrategy(string validatorName)
        {
            return (validatorStrategies.SingleOrDefault(s => s.Metadata.ValidationAttribute == validatorName) ?? new Lazy<IValidatorStrategy, IValidatorStrategyData>(() => new UnknownValidatorStrategy(), null)).Value;
        }

        private static string AssemblyDirectory
        {
            get
            {
                var codeBase = Assembly.GetExecutingAssembly().CodeBase;
                var uri = new UriBuilder(codeBase);
                var path = Uri.UnescapeDataString(uri.Path);
                return Path.GetDirectoryName(path);
            }
        }
    }


    [Export(typeof(IValidatorStrategy))]
    [ExportMetadata("ValidationAttribute", "RequiredAttribute")]
    class RequiredValidatorStrategy : IValidatorStrategy
    {
        public string WriteExtender(JToken jToken, IList<CustomAttributeTypedArgument> ctorArgs, IList<CustomAttributeNamedArgument> namedArgs, ContractResolvers contractResolver = ContractResolvers.Default)
        {
            return string.Format("{{\"required\":{0}}}",namedArgs.ErrorMessageOrParameters(true));
        }
    }

    [Export(typeof(IValidatorStrategy))]
    [ExportMetadata("ValidationAttribute", "MinLengthAttribute")]
    class MinLengthValidatorStrategy : IValidatorStrategy
    {
        public string WriteExtender(JToken jToken, IList<CustomAttributeTypedArgument> ctorArgs, IList<CustomAttributeNamedArgument> namedArgs, ContractResolvers contractResolver = ContractResolvers.Default)
        {
            return string.Format("{{\"minLength\":{0}}}", namedArgs.ErrorMessageOrParameters((int)ctorArgs[0].Value));
        }
    }

    [Export(typeof(IValidatorStrategy))]
    [ExportMetadata("ValidationAttribute", "MaxLengthAttribute")]
    class MaxLengthValidatorStrategy : IValidatorStrategy
    {
        public string WriteExtender(JToken jToken, IList<CustomAttributeTypedArgument> ctorArgs, IList<CustomAttributeNamedArgument> namedArgs, ContractResolvers contractResolver = ContractResolvers.Default)
        {
            return string.Format("{{\"maxLength\":{0}}}", namedArgs.ErrorMessageOrParameters((int)ctorArgs[0].Value));
        }
    }

    [Export(typeof(IValidatorStrategy))]
    [ExportMetadata("ValidationAttribute", "RangeAttribute")]
    class RangeValidatorStrategy : IValidatorStrategy
    {
        public string WriteExtender(JToken jToken, IList<CustomAttributeTypedArgument> ctorArgs, IList<CustomAttributeNamedArgument> namedArgs, ContractResolvers contractResolver = ContractResolvers.Default)
        {
            return string.Format("{{\"min\":{0},\"max\":{1}}}", namedArgs.ErrorMessageOrParameters((int) ctorArgs[0].Value), namedArgs.ErrorMessageOrParameters((int) ctorArgs[1].Value));
        }
    }

    [Export(typeof(IValidatorStrategy))]
    [ExportMetadata("ValidationAttribute", "RegularExpressionAttribute")]
    class RegularExpressionValidatorStrategy : IValidatorStrategy
    {
        public string WriteExtender(JToken jToken, IList<CustomAttributeTypedArgument> ctorArgs, IList<CustomAttributeNamedArgument> namedArgs, ContractResolvers contractResolver = ContractResolvers.Default)
        {
            return string.Format("{{\"pattern\":{0}}}", namedArgs.ErrorMessageOrParameters(ctorArgs[0].Value.ToString().Replace("\\","\\\\") ));
        }
    }

    [Export(typeof(IValidatorStrategy))]
    [ExportMetadata("ValidationAttribute", "EmailAddressAttribute")]
    class EmailAddressStrategy : IValidatorStrategy
    {
        public string WriteExtender(JToken jToken, IList<CustomAttributeTypedArgument> ctorArgs, IList<CustomAttributeNamedArgument> namedArgs, ContractResolvers contractResolver = ContractResolvers.Default)
        {
            return string.Format("{{\"email\":{0}}}", namedArgs.ErrorMessageOrParameters(true));
        }
    }

    [Export(typeof(IValidatorStrategy))]
    [ExportMetadata("ValidationAttribute", "CompareAttribute")]
    class CompareValidatorStrategy : IValidatorStrategy
    {
        public string WriteExtender(JToken jToken, IList<CustomAttributeTypedArgument> ctorArgs, IList<CustomAttributeNamedArgument> namedArgs, ContractResolvers contractResolver = ContractResolvers.Default)
        {
            var field = contractResolver == ContractResolvers.CamelCase
                ? ctorArgs[0].Value.ToString().ToLower()
                : ctorArgs[0].Value.ToString();

            return string.Format("{{\"areSame\":{0}}}", namedArgs.ErrorMessageOrParameters(field));
        }
    }

    static class CustomAttributeNamedArgumentExtensions
    {
        public static string ErrorMessageOrParameters<T>(this IList<CustomAttributeNamedArgument> argList, T parameters)
        {
            var paramType = typeof(T);
            var paramString = paramType == typeof(bool) ? parameters.ToString().ToLower() : parameters.ToString();

            if (argList.All(a => a.MemberName != "ErrorMessage"))
            {
                return paramString;
            }

            var messageFormat = paramType == typeof(string) ? "{{\"message\":\"{0}\",\"params\":\"{1}\"}}" : "{{\"message\":\"{0}\",\"params\":{1}}}";

            return string.Format(messageFormat, argList.Single(a => a.MemberName == "ErrorMessage").TypedValue.Value, paramString);            
        }                
    }
}
